package com.supreme.basicserver.config;

import com.alibaba.druid.pool.DruidDataSource;
import com.google.common.collect.Lists;
import com.supreme.commonboot.utils.MysqlInfo;
import org.apache.shardingsphere.api.config.masterslave.MasterSlaveRuleConfiguration;
import org.apache.shardingsphere.api.config.sharding.KeyGeneratorConfiguration;
import org.apache.shardingsphere.api.config.sharding.ShardingRuleConfiguration;
import org.apache.shardingsphere.api.config.sharding.TableRuleConfiguration;
import org.apache.shardingsphere.api.config.sharding.strategy.InlineShardingStrategyConfiguration;
import org.apache.shardingsphere.shardingjdbc.api.ShardingDataSourceFactory;
import org.springframework.context.annotation.Bean;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.*;

//@Configuration
public class ShardingSphereConfig {

    private final ShardingRuleConfiguration shardingRuleConfig;

    public ShardingSphereConfig() {
        this.shardingRuleConfig = new ShardingRuleConfiguration();
    }

    @Bean
    public DataSource dataSourceConfig() throws SQLException {
        createDataSourceMap();

        setSysSettingTableRuleConfig();
        setBizOrderTableRuleConfig();
        setBizOrderItemTableRuleConfig();

        MysqlInfo productConfig = new MysqlInfo("ds${0}", "biz_product", "product_id");
        MysqlInfo qualityConfig = new MysqlInfo("ds${1}", "biz_quality", "quality_id");
        setVerticalDatabaseConfig(productConfig, qualityConfig);

        setBroadcastTableConfig("com_dictionary");

        MysqlInfo MysqlInfo = new MysqlInfo("ds${0..1}", "sys_user", "user_id");
        setDefaultDatabaseConfig(MysqlInfo);

        setMasterSlaveDatabaseConfig();

        Properties properties = new Properties();
        properties.setProperty("sql.show", "true");
        return ShardingDataSourceFactory.createDataSource(createDataSourceMap(), this.shardingRuleConfig, properties);
    }

    /**
     * sys_setting 分表配置
     */
    private void setSysSettingTableRuleConfig() {
        TableRuleConfiguration settingTableRuleConfig = new TableRuleConfiguration("sys_setting","ds${0}.sys_setting_${0..1}");

        // 配置分表策略
        settingTableRuleConfig.setTableShardingStrategyConfig(new InlineShardingStrategyConfiguration("setting_id", "sys_setting_${setting_id % 2}"));
        settingTableRuleConfig.setKeyGeneratorConfig(new KeyGeneratorConfiguration("SNOWFLAKE", "setting_id"));

        this.shardingRuleConfig.getTableRuleConfigs().add(settingTableRuleConfig);
    }

    /**
     * biz_order 分库分表配置
     */
    private void setBizOrderTableRuleConfig() {
        TableRuleConfiguration orderTableRuleConfig = new TableRuleConfiguration("biz_order","ds${0..1}.biz_order_${0..1}");

        // 配置分库 + 分表策略
        orderTableRuleConfig.setDatabaseShardingStrategyConfig(new InlineShardingStrategyConfiguration("order_type", "ds${order_type % 2}"));
        orderTableRuleConfig.setTableShardingStrategyConfig(new InlineShardingStrategyConfiguration("order_id", "biz_order_${order_id % 2}"));
        orderTableRuleConfig.setKeyGeneratorConfig(new KeyGeneratorConfiguration("SNOWFLAKE", "order_id"));

        this.shardingRuleConfig.getTableRuleConfigs().add(orderTableRuleConfig);
    }

    /**
     * biz_order_item 分库分表配置
     */
    private void setBizOrderItemTableRuleConfig() {
        TableRuleConfiguration orderTableRuleConfig = new TableRuleConfiguration("biz_order_item","ds${0..1}.biz_order_item_${0..1}");

        // 配置分库 + 分表策略
        orderTableRuleConfig.setDatabaseShardingStrategyConfig(new InlineShardingStrategyConfiguration("order_type", "ds${order_type % 2}"));
        orderTableRuleConfig.setTableShardingStrategyConfig(new InlineShardingStrategyConfiguration("order_id", "biz_order_item_${order_id % 2}"));
        orderTableRuleConfig.setKeyGeneratorConfig(new KeyGeneratorConfiguration("SNOWFLAKE", "order_item_id"));

        this.shardingRuleConfig.getTableRuleConfigs().add(orderTableRuleConfig);
        this.shardingRuleConfig.getBindingTableGroups().add("biz_order, biz_order_item");
    }

    /**
     * 垂直分表配置
     */
    private void setVerticalDatabaseConfig(MysqlInfo... MysqlInfos) {
        for (MysqlInfo MysqlInfo : MysqlInfos) {
            String databaseName = MysqlInfo.getDatabaseName();
            String tableName = MysqlInfo.getTableName();
            TableRuleConfiguration tableRuleConfiguration = new TableRuleConfiguration(tableName, String.format("%s.%s", databaseName, tableName));

            if (MysqlInfo.getPrimaryKey() != null) {
                String primaryKey = MysqlInfo.getPrimaryKey();
                tableRuleConfiguration.setKeyGeneratorConfig(new KeyGeneratorConfiguration("SNOWFLAKE", primaryKey));
            }
            this.shardingRuleConfig.getTableRuleConfigs().add(tableRuleConfiguration);
        }
    }

    /**
     * 公共表配置
     * @param tableNames
     */
    private void setBroadcastTableConfig(String... tableNames) {
        for (String tableName : tableNames) {
            this.shardingRuleConfig.getBroadcastTables().add(tableName);
        }
    }

    /**
     * 默认分表策略配置
     */
    private void setDefaultDatabaseConfig(MysqlInfo... MysqlInfos) {
        List<TableRuleConfiguration> tableRuleConfigurationList = new ArrayList<>();
        for (MysqlInfo MysqlInfo : MysqlInfos) {
            String databaseName = MysqlInfo.getDatabaseName();
            String tableName = MysqlInfo.getTableName();
            TableRuleConfiguration tableRuleConfiguration = new TableRuleConfiguration(tableName, String.format("%s.%s", databaseName, tableName));

            if (MysqlInfo.getPrimaryKey() != null) {
                String primaryKey = MysqlInfo.getPrimaryKey();
                tableRuleConfiguration.setKeyGeneratorConfig(new KeyGeneratorConfiguration("SNOWFLAKE", primaryKey));
            }
            tableRuleConfigurationList.add(tableRuleConfiguration);
        }

        this.shardingRuleConfig.getTableRuleConfigs().addAll(tableRuleConfigurationList);

        InlineShardingStrategyConfiguration strategyConfiguration = new InlineShardingStrategyConfiguration("domain_id", "ds${domain_id % 2}");
        this.shardingRuleConfig.setDefaultDatabaseShardingStrategyConfig(strategyConfiguration);
    }

    /**
     * 读写分离配置
     */
    private void setMasterSlaveDatabaseConfig() {
        MasterSlaveRuleConfiguration masterSlaveRuleConfig1 = new MasterSlaveRuleConfiguration("ds0", "ds0", Arrays.asList("dl13", "dl14"));
        MasterSlaveRuleConfiguration masterSlaveRuleConfig2 = new MasterSlaveRuleConfiguration("ds1", "ds1", Arrays.asList("dl11", "dl12"));

        ArrayList<MasterSlaveRuleConfiguration> masterSlaveRuleConfigurations = Lists.newArrayList(masterSlaveRuleConfig1, masterSlaveRuleConfig2);
        this.shardingRuleConfig.setMasterSlaveRuleConfigs(masterSlaveRuleConfigurations);
    }

    private DataSource createDataSource(String host, int port, String dataName, String username, String password) {
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        druidDataSource.setUrl(String.format("jdbc:mysql://%s:%s/%s?useUnicode=true&characterEncoding=utf-8&useSSL=false", host, port, dataName));
        druidDataSource.setUsername(username);
        druidDataSource.setPassword(password);
        return druidDataSource;
    }

    Map<String, DataSource> createDataSourceMap() {
        Map<String, DataSource> result = new HashMap<>();
        result.put("ds0", createDataSource("192.168.31.186", 3310, "demo_db", "root", "111111"));
        result.put("dl13", createDataSource("192.168.31.186", 3313, "demo_db", "root", "111111"));
        result.put("dl14", createDataSource("192.168.31.186", 3314, "demo_db", "root", "111111"));
        result.put("ds1", createDataSource("192.168.31.186", 3309, "demo_db", "root", "111111"));
        result.put("dl11", createDataSource("192.168.31.186", 3311, "demo_db", "root", "111111"));
        result.put("dl12", createDataSource("192.168.31.186", 3312, "demo_db", "root", "111111"));
        return result;
    }
}
